package com.ccc.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.ccc.dto.Result;
import com.ccc.entity.VoucherOrder;
import com.ccc.mapper.VoucherOrderMapper;
import com.ccc.service.ISeckillVoucherService;
import com.ccc.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ccc.utils.RedisIdWorker;
import com.ccc.utils.SimpleRedisLock;
import com.ccc.utils.UserHolder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Resource
    private RedisIdWorker redisIdWorker;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    public  Result  seckillVoucher(Long voucherId) throws InterruptedException {
        //实现下单功能
        Long userId = UserHolder.getUser().getId();
        Long orderId =  redisIdWorker.nextId("order");
        Long success = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(),
                userId.toString(),
               orderId.toString()
        );
        if (success.intValue() != 0) {
            return Result.fail(success == 1?"优惠券已被抢光!":"每人限领一次~");
        }
        //以上, 完成订单库存,用户资格判断,发送订单到消息队列(前提:订单需要加载到缓存)

        //异步执行消息队列读取与数据库更新
        Thread thread = new Thread(() -> {
            while(true) {
                try {
                    List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(1)),
                            StreamOffset.create("stream.orders", ReadOffset.lastConsumed()));
                    if (list == null || list.isEmpty()) {
                        continue;
                    }
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(list.get(0).getValue(), new VoucherOrder(), true);
                    //保存到redis
                    handleVoucherOrder(voucherOrder);
                    //ack
                    stringRedisTemplate.opsForStream().acknowledge("stream.order","g1",list.get(0).getId());
                }
                catch (Exception e) {
                    handlePendingList();
                }
            }
        });
        thread.start();
        return Result.ok(orderId);
    }

    private void handlePendingList() {
        while(true) {
            try {
                List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                        Consumer.from("g1", "c1"),
                        StreamReadOptions.empty().count(1),
                        StreamOffset.create("stream.orders", ReadOffset.from("0")));
                if (list == null || list.isEmpty()) {
                    break;
                }
                VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(list.get(0).getValue(), new VoucherOrder(), true);
                //保存到redis
                handleVoucherOrder(voucherOrder);
                //ack
                stringRedisTemplate.opsForStream().acknowledge("stream.order","g1",list.get(0).getId());
            }
            catch (Exception e) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }
    }

    //    RLock lock = redissonClient.getLock("lock:order:"+userId);
//    boolean isLock = lock
//            .tryLock();
//        if(!isLock){
//        return Result.fail("繁忙,重什!");
//    }
//        try {
//        IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
//        return proxy.createVoucherOrder(voucherId);
//    }
//        finally {
//        lock.unlock();
//    }
    public void handleVoucherOrder(VoucherOrder voucherOrder) {//已下单
        save(voucherOrder);
    }
//    SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
//    //首先判断时间是否合法
//        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
//        return Result.fail("秒杀活动未开始!");
//    }
//        else if(voucher.getEndTime().isBefore(LocalDateTime.now())){
//        return Result.fail("秒杀活动已结束!");
//    }
//    //判断库存为空
//        if(voucher.getStock()<=0){
//        return Result.fail("优惠券已被抢光!");
//    }
//    String userId = UserHolder.getUser().getId().toString();
//
//    //新增锁对象
//    //SimpleRedisLock simpleRedisLock = new SimpleRedisLock("order:"+userId, stringRedisTemplate);
//    RLock lock = redissonClient.getLock("lock:order:"+userId);
//    boolean success = lock.tryLock(1,10, TimeUnit.SECONDS);
//        if(!success){
//        return Result.fail("您已经领取过了!");
//    }
//        try {
//        IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
//        return proxy.createVoucherOrder(voucherId);
//    }
//        finally {
//        lock.unlock();
//    }
}
